﻿/* 
 * Copyright (c) 2009-2013, Andriy Syrov
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided 
 * that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of conditions and the 
 * following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
 * the following disclaimer in the documentation and/or other materials provided with the distribution.
 *
 * Neither the name of Andriy Syrov nor the names of his contributors may be used to endorse or promote 
 * products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY 
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 *   
 */

namespace Codeplex.InertialScroll
{
    using System;
    using System.Diagnostics;

    /// 
    /// <summary>
    /// State of inertial scroll</summary>
    /// 
    public class TraversalState
    {
        public const double                             CONSTANT_SPEED_TIME = 50; 
        public const double                             ACCELERATED_TIME    = 2000;
 
        public const double                             TOTAL_TIME = ACCELERATED_TIME * 2 + CONSTANT_SPEED_TIME;

        public double                                   Speed;
        public double                                   Acceleration;

        public double                                   StartPosition;
        public double                                   EndPosition;

        public double                                   CurrentTime;
        public double                                   CurrentPosition;

        public bool                                     Finished;

        public TraversalState(
            double                                      start,
            double                                      stop
        )
        {


            StartPosition       = start;
            EndPosition         = stop;
            CurrentPosition     = start;
            CurrentTime         = 0;

            Acceleration = (stop - start) / (ACCELERATED_TIME * ACCELERATED_TIME + CONSTANT_SPEED_TIME * ACCELERATED_TIME);
            Speed = Acceleration * ACCELERATED_TIME;
        }

        public void Finish(
        )
        {
            Finished        = true;
            CurrentPosition = EndPosition;
        }
    }

    /// 
    /// <summary>
    /// Inertial scroll calculator</summary>
    /// 
    public class TraversalScroll
    {
        public virtual TraversalState Calculate(
            double                                       timeDelta,
            TraversalState                               state
        )
        {
            double current     = state.CurrentTime + timeDelta;
            state.CurrentTime += timeDelta;

            if (current > TraversalState.TOTAL_TIME)
            {
                state.Finish();
                Debug.WriteLine("finished:");
            }
            else if (current <= TraversalState.ACCELERATED_TIME)
            {
                state.CurrentPosition  = (state.Acceleration * (current * current)) / 2.0;
                state.CurrentPosition += state.StartPosition;
            }
            else if (current <= TraversalState.ACCELERATED_TIME + TraversalState.CONSTANT_SPEED_TIME)
            {
                state.CurrentPosition  = (state.Acceleration * (TraversalState.ACCELERATED_TIME * TraversalState.ACCELERATED_TIME)) / 2.0;
                state.CurrentPosition += state.Speed * (current - TraversalState.ACCELERATED_TIME);
                state.CurrentPosition += state.StartPosition;
            }
            else 
            {
                state.CurrentPosition  = (state.Acceleration * (TraversalState.ACCELERATED_TIME * TraversalState.ACCELERATED_TIME)) / 2.0;
                state.CurrentPosition += state.Speed * TraversalState.CONSTANT_SPEED_TIME;

                var decelTime = current - (TraversalState.ACCELERATED_TIME + TraversalState.CONSTANT_SPEED_TIME);
                state.CurrentPosition += state.Speed * decelTime - state.Acceleration * (decelTime * decelTime) / 2;
                state.CurrentPosition += state.StartPosition;
            }

            return state;
        }
    }
}

